package com.fw.util;

import java.math.BigInteger;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;

import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;

public class PasswordUtils {

    public static final int SALT_BYTE_SIZE = 64;
    public static final int HASH_BYTE_SIZE = 64;
    public static final int PBKDF2_ITERATIONS = 1000;

    public static String generateSecuredPasswordHash(String password) throws NoSuchAlgorithmException, InvalidKeySpecException {
	char[] chars = password.toCharArray();
	byte[] salt = getSecuredSalt().getBytes();

	PBEKeySpec spec = new PBEKeySpec(chars, salt, PBKDF2_ITERATIONS, HASH_BYTE_SIZE * 8);
	SecretKeyFactory skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
	byte[] hash = skf.generateSecret(spec).getEncoded();
	return toHex(hash) + ":" + PBKDF2_ITERATIONS + ":" + toHex(salt);
    }

    public static boolean validatePassword(String originalPassword, String storedPassword) throws NoSuchAlgorithmException,
	    InvalidKeySpecException {
	String[] parts = storedPassword.split(":");
	int iterations = Integer.parseInt(parts[1]);
	byte[] salt = fromHex(parts[2]);
	byte[] hash = fromHex(parts[0]);

	PBEKeySpec spec = new PBEKeySpec(originalPassword.toCharArray(), salt, iterations, hash.length * 8);
	SecretKeyFactory skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
	byte[] testHash = skf.generateSecret(spec).getEncoded();

	int diff = hash.length ^ testHash.length;
	for (int i = 0; i < hash.length && i < testHash.length; i++) {
	    diff |= hash[i] ^ testHash[i];
	}
	return diff == 0;
    }

    private static final String getSecuredSalt() {

	SecureRandom random = new SecureRandom();
	byte[] salt = new byte[SALT_BYTE_SIZE];
	random.nextBytes(salt);
	return salt.toString();
    }

    private static final String toHex(byte[] array) throws NoSuchAlgorithmException {
	BigInteger bi = new BigInteger(1, array);
	String hex = bi.toString(16);
	int paddingLength = (array.length * 2) - hex.length();
	if (paddingLength > 0) {
	    return String.format("%0" + paddingLength + "d", 0) + hex;
	} else {
	    return hex;
	}

    }

    private static final byte[] fromHex(String hex) throws NoSuchAlgorithmException {
	byte[] bytes = new byte[hex.length() / 2];
	for (int i = 0; i < bytes.length; i++) {
	    bytes[i] = (byte) Integer.parseInt(hex.substring(2 * i, 2 * i + 2), 16);
	}
	return bytes;
    }

}
